# 作者：徐宇杰
# -*- coding = utf-8 -*-
# @Time :2024/7/29 下午2:03
# @File:BackTestversion1.py
# @Software:PyCharm
import json
import sys
import warnings
from datetime import date, timedelta

import numpy as np
import pandas as pd
import requests

pd.set_option('float_format', lambda x: '%.2f' % x)

warnings.simplefilter(action='ignore', category=FutureWarning)

# 数据传输的初步处理
# Token accessToken 及权限校验机制
getAccessTokenUrl = 'https://quantapi.51ifind.com/api/v1/get_access_token'
# 获取refresh_token需下载Windows版本接口包解压，打开超级命令-工具-refresh_token查询
refreshtoken = ('eyJzaWduX3RpbWUiOiIyMDI0LTA3LTMwIDEyOjM2OjI0In0=.eyJ1aWQiOiI3MDQyMzg2NjQiLCJ1c2VyIjp7ImFjY291bnQiOiJkZHFoMTE4IiwiYXV0aFVzZXJJbmZvIjp7fSwiY29kZUNTSSI6W10sImNvZGVaekF1dGgiOltdLCJoYXNBSVByZWRpY3QiOmZhbHNlLCJoYXNBSVRhbGsiOmZhbHNlLCJoYXNDSUNDIjpmYWxzZSwiaGFzQ1NJIjpmYWxzZSwiaGFzRXZlbnREcml2ZSI6ZmFsc2UsImhhc0ZUU0UiOmZhbHNlLCJoYXNGYXN0IjpmYWxzZSwiaGFzRnVuZFZhbHVhdGlvbiI6ZmFsc2UsImhhc0hLIjp0cnVlLCJoYXNMTUUiOmZhbHNlLCJoYXNMZXZlbDIiOmZhbHNlLCJoYXNSZWFsQ01FIjpmYWxzZSwiaGFzVHJhbnNmZXIiOmZhbHNlLCJoYXNVUyI6ZmFsc2UsImhhc1VTQUluZGV4IjpmYWxzZSwibWFya2V0Q29kZSI6IjE2OzMyOzE0NDsxNzY7MTEyOzg4OzQ4OzEyODsxNjgtMTsxODQ7MjAwOzIxNjsxMDQ7MTIwOzEzNjsyMzI7NTY7OTY7MTYwOzY0OyIsIm1heE9uTGluZSI6MSwibm9EaXNrIjpmYWxzZSwicHJvZHVjdFR5cGUiOiJTVVBFUkNPTU1BTkRQUk9EVUNUIiwicmVmcmVzaFRva2VuRXhwaXJlZFRpbWUiOiIyMDI0LTA4LTE3IDExOjUwOjUwIiwic2Vzc3Npb24iOiJhNTZhZmFmMDJmOWQ5ZTA3OGM5MzJlYjgxYjIzN2Y1MSIsInNpZEluZm8iOnt9LCJ0cmFuc0F1dGgiOmZhbHNlLCJ1aWQiOiI3MDQyMzg2NjQiLCJ1c2VyVHlwZSI6IkZSRUVJQUwiLCJ3aWZpbmRMaW1pdE1hcCI6e319fQ==.87537C0D857B49683B7300DF7F0F9798A0FE7D527743E2DBC329325BAC83E42F')
getAccessTokenHeader = {"Content- Type": "application/json", "refresh_token": refreshtoken}
getAccessTokenResponse = requests.post(url=getAccessTokenUrl, headers=getAccessTokenHeader)
accessToken = json.loads(getAccessTokenResponse.content)['data']['access_token']
thsHeaders = {"Content-Type": "application/json", "access_token": accessToken}


# 输入的参数
# 1.品种（现在只支持上海期货交易所的那只主连期货），例如 "RB"（螺纹钢）、"RU"（橡胶）等（字符串类型）
# 这个版本是输入code,为品种对应的主连代码，如RBZL.SHF（螺纹钢），（黄金、白银没有测试实现）
# 2.开始日期与结束日期(start_date,end_date)(需要字符串类型) 如"2023-08-14 10:00:00"   "2024-07-01 10:00:00"，第一天不能在到期日期内
# 3.数据的频率(frequency) 如1、30等（表示一分钟、三十分钟） 整数类型，默认为30
# 4.期权选择的方法(option_selection) 不存在到期日，对于到期日前五天的数据直接用下一个指标的期权代替
# 默认为最高最低四分之一百位取整数的方法,方法0为默认方法，range_window  整数类型  参数 为取过去多长间隔的数据来采取回推
# 5.止损和止盈的标准(profit_stop,loss_stop) 默认为0.1 和 -0.1 （到期日不计算也为默认,已经在里面实现了）
# 6.分批(quant)功能的实现，如果分批，输入分批的数目（最大值），默认值为1，也就是不分批
def initialization(thsResponse):
    data0 = json.loads(thsResponse.content)
    data1 = pd.json_normalize(data0['tables'])
    un_nested_lst = []
    for col in data1.columns:
        un_nested_lst.append(data1[col].apply(pd.Series).stack())
    data2 = pd.concat(un_nested_lst, axis=1, keys=data1.columns)
    data2 = data2.reset_index(drop=True)
    return data2


def Import_Futures_Data(code, start_date, end_date, frequency):
    print("导入主连期货的数据")
    thsUrl = 'https://quantapi.51ifind.com/api/v1/high_frequency'
    thsPara = {"codes": code,
               "indicators":
                   "open,high,low,close",
               "starttime": start_date,
               "endtime": end_date,
               "functionpara": {
                   "Interval": str(frequency),
                   "Fill": "Original"
               }
               }
    thsResponse = requests.post(url=thsUrl, json=thsPara, headers=thsHeaders)
    data = json.loads(thsResponse.content)
    df = pd.json_normalize(data['tables'])
    try:
        df2 = df.set_index(['thscode'])
    except Exception as e:
        print(data['errmsg'])
        print(thsPara)
        sys.exit()
    un_nested_list = []
    for col in df2.columns:
        un_nested_list.append(df2[col].apply(pd.Series).stack())
    data = pd.concat(un_nested_list, axis=1, keys=df2.columns)
    # 设置二级索引
    data = data.reset_index()
    data = data.set_index(['thscode', 'time'])
    # 格式化,行转列
    data = data.drop(columns=['level_1'])
    data = data.reset_index()
    # 设置列索引名称
    data.columns = ['thscode', 'time', 'close', 'open', 'high', 'low']
    # 去除NaN值
    data.dropna(inplace=True)
    data = data.reset_index()
    data = data.iloc[:, 1:]
    return data


def Import_Local_Data(code, local_path):
    print("导入本地数据阶段")
    if local_path is None:
        print("请输入正确的路径")
        sys.exit()
    data0 = pd.read_excel(local_path, dtype={'Key_Month': str})
    mask = data0['Thscode'] == code
    price_change = data0.loc[mask, 'Tick Size'].iloc[0]
    key_months = data0.loc[mask, 'Key_Month'].iloc[0]
    contract_multiplier = data0.loc[mask, 'Contract_Multiplier'].iloc[0]
    return price_change, key_months, contract_multiplier


def Identifying_Options(data, code, price_change, key_months, range_window):
    print("依据数据筛选期权阶段")
    date_frame = pd.DataFrame(columns=['fore_code', 'over_date'])
    date_flag = 0
    df = pd.DataFrame(columns=['C_code', 'P_code', 'time', 'close', 'upper', 'lower', 'k_call', 'k_put'])
    list0 = key_months.split(',')
    length0 = len(list0)
    if length0 == 3:
        key_month0 = list0[2]
    flag = 0
    # 到期日前五天不做
    day_limit = 5
    for i in range(0, len(data) - range_window):
        data_temp = data.iloc[i:i + range_window, ]  # 迭代取观测区间点
        timestamp_open = data_temp.iloc[-1, 1]
        # 日期的计算
        index1 = str(timestamp_open).find('-')
        year = int(str(timestamp_open)[:index1]) - 2000
        index2 = str(timestamp_open).find('-', index1 + 1)
        month = int(str(timestamp_open)[index1 + 1:index2])
        date_str1 = str(timestamp_open)[:10]
        date1 = date.fromisoformat(date_str1)
        # 设置期权名称的日期
        if length0 == 3:
            if month < 5:
                month = '05'
            elif month < int(key_month0):
                month = key_month0
            else:
                month = '01'
                year = year + 1
        else:
            if month == 12:
                year = year + 1
                month = list0[0]
            else:
                month = list0[month]
        # 计算上下界限
        upper = data_temp['high'].max()
        lower = data_temp['low'].min()
        # 使得期权选择能够选择所有
        # 计算期权名称的执行价，有时候是多少以下一个变化，多少以上另一个变化，可以读取进来两个，根据值来判断，还需要设置一个（没有实现）
        UpDown = upper - lower
        otm_range = int(UpDown / 4)
        price1 = round(data_temp.iloc[-1, 2] + otm_range)
        strike_price1 = int(price1 / price_change) * price_change
        while abs(strike_price1 - price1) > (price_change / 2):
            strike_price1 = strike_price1 + price_change
        K_call = int(strike_price1)
        price2 = round(data_temp.iloc[-1, 2] - otm_range)
        strike_price2 = int(price2 / price_change) * price_change
        while abs(strike_price2 - price2) > (price_change / 2):
            strike_price2 = strike_price2 + price_change
        K_put = int(strike_price2)
        if code[-3:] == "SHF":
            new_string = code.replace("ZL", "")
            str_list = list(new_string)
            str_list.insert(-4, "{}C{}".format(str(year) + month, K_call))
        elif code[-3:] == "CZC":
            new_string = code.replace("ZL", "")
            str_list = list(new_string)
            year = year % 10
            str_list.insert(-4, "{}C{}".format(str(year) + month, K_call))
        else:
            new_string = code.replace("ZL", "")
            str_list = list(new_string)
            str_list.insert(-4, "{}-C-{}".format(str(year) + month, K_call))
        code_call = ''.join(str_list)
        previous_day = date1 - timedelta(days=1)
        previous = previous_day.strftime("%Y-%m-%d")
        # 利用http调用股权的到期日日期
        if flag == 0 or df.loc[flag - 1, 'C_code'][:6] != code_call[:6]:
            # 在这里加一个判断语句
            thsUrl = 'https://quantapi.51ifind.com/api/v1/date_sequence'
            thsPara = {"codes": code_call,
                       "startdate": previous,
                       "enddate": date_str1,
                       "functionpara":
                           {"Fill": "Blank"},
                       "indipara":
                           [
                               {
                                   "indicator":
                                       "ths_last_strike_date_option"
                               }
                           ]
                       }
            thsResponse = requests.post(url=thsUrl, json=thsPara, headers=thsHeaders)
            try:
                str_date1 = json.loads(thsResponse.content)
            except Exception as e:
                print(e, thsPara)
                sys.exit()
            str_date2 = pd.json_normalize(str_date1['tables'])
            str_date = str_date2.reset_index(drop=True)
            try:
                date_str = str_date.iloc[0, 2]
                date_str2 = date_str[0]
            except Exception as e:
                print(e, thsPara, str_date1)
                sys.exit()
            date2 = date.fromisoformat(date_str2)
            date3 = date2 - timedelta(days=day_limit)
            over_date = date2.strftime("%Y-%m-%d")
            if date_flag == 0 or over_date != date_frame.iloc[date_flag - 1, 1]:
                time_row = {'fore_code': code_call[:6], 'over_date': over_date}
                date_frame.loc[date_flag] = time_row
                date_flag = date_flag + 1
        if date1 < date3:
            if code[-3:] == "SHF":
                new_string = code.replace("ZL", "")
                str_list = list(new_string)
                str_list.insert(-4, "{}P{}".format(str(year) + month, K_put))
            elif code[-3:] == "CZC":
                new_string = code.replace("ZL", "")
                str_list = list(new_string)
                year = year % 10
                str_list.insert(-4, "{}P{}".format(str(year) + month, K_put))
            else:
                new_string = code.replace("ZL", "")
                str_list = list(new_string)
                str_list.insert(-4, "{}-P-{}".format(str(year) + month, K_put))
            code_put = ''.join(str_list)
            new_row = {'C_code': code_call, 'P_code': code_put, 'close': data_temp.iloc[-1, 2], 'upper': upper,
                       'lower': lower, 'time': timestamp_open,
                       'k_call': K_call, 'k_put': K_put
                       }
            df.loc[flag] = new_row
        else:
            if length0 == 3:
                t_year = year
                if month == '05':
                    t_month = key_month0
                elif month == key_month0:
                    t_month = '01'
                    t_year = year + 1
                else:
                    t_month = '05'
            else:
                t_year = year
                if month == "12":
                    t_year = year + 1
                    t_month = list0[0]
                else:
                    t_month = list0[int(month)]
            if code[-3:] == "SHF":
                new_string = code.replace("ZL", "")
                str_list = list(new_string)
                str_list.insert(-4, "{}C{}".format(str(t_year) + t_month, K_call))
                t_code_call = ''.join(str_list)
                str_list = list(new_string)
                str_list.insert(-4, "{}P{}".format(str(t_year) + t_month, K_put))
                t_code_put = ''.join(str_list)
            elif code[-3:] == "CZC":
                new_string = code.replace("ZL", "")
                str_list = list(new_string)
                t_year = t_year % 10
                str_list.insert(-4, "{}C{}".format(str(t_year) + t_month, K_call))
                t_code_call = ''.join(str_list)
                str_list = list(new_string)
                str_list.insert(-4, "{}P{}".format(str(t_year) + t_month, K_put))
                t_code_put = ''.join(str_list)
            else:
                new_string = code.replace("ZL", "")
                str_list = list(new_string)
                str_list.insert(-4, "{}-C-{}".format(str(t_year) + t_month, K_call))
                t_code_call = ''.join(str_list)
                str_list = list(new_string)
                str_list.insert(-4, "{}-P-{}".format(str(t_year) + t_month, K_put))
                t_code_put = ''.join(str_list)
            new_row = {'C_code': t_code_call, 'P_code': t_code_put, 'close': data_temp.iloc[-1, 2], 'upper': upper,
                       'lower': lower, 'time': timestamp_open,
                       'k_call': K_call, 'k_put': K_put
                       }
            df.loc[flag] = new_row
        flag = flag + 1
    return df, date_frame


def get_the_margin_price(str_date, code):
    deposit = 0
    thsUrl = 'https://quantapi.51ifind.com/api/v1/date_sequence'
    thsPara = {"codes": code,
               "startdate": str_date,
               "enddate": str_date,
               "functionpara":
                   {"Fill": "Blank"},
               "indipara":
                   [
                       {
                           "indicator":
                               "ths_trade_deposit_option"
                       }
                   ]
               }
    thsResponse = requests.post(url=thsUrl, json=thsPara, headers=thsHeaders)
    data2 = initialization(thsResponse)
    deposit = data2.iloc[0, 2]
    return deposit


def get_the_option_data(code, start_date, close_date, frequency, i, df, date_frame, type0):
    length = len(df)
    thsUrl = 'https://quantapi.51ifind.com/api/v1/high_frequency'
    thsPara = {"codes": code,
               "indicators": "close",
               "starttime": start_date,
               "endtime": close_date,
               "functionpara": {
                   "Interval": str(frequency),
                   "Fill": "Original"
               }
               }
    thsResponse = requests.post(url=thsUrl, json=thsPara, headers=thsHeaders)
    data2 = initialization(thsResponse)
    data2 = data2.iloc[:, 1:]
    length0 = len(data2)
    min_length = min(length - i, length0)
    day_limit1 = date_frame.loc[date_frame['fore_code'] == code[:6], 'over_date']
    day_limit = date.fromisoformat(day_limit1.tolist()[0])
    recent_index = i + 1
    if type0 == 0:
        x = 10
    else:
        x = 13
    for j in range(0, min_length - 1):
        if j == 0:
            df.iloc[recent_index - 1, x] = data2.iloc[j, 1]
        if date.fromisoformat(data2.iloc[j, 0][:10]) >= day_limit:
            break
        min_allowed_count = length - recent_index
        for m in range(0, min_allowed_count):
            if date.fromisoformat(df.iloc[recent_index + m, 2][:10]) >= day_limit:
                break
            if df.iloc[recent_index + m, 2] != data2.iloc[j + 1, 0]:
                # 没找到对应时间
                df.iloc[recent_index + m, x] = data2.iloc[j, 1]
                # 到最后一个时间点
                if recent_index + m == length - 1:
                    break
            else:
                # 找到对应的时间
                df.iloc[recent_index + m, x] = data2.iloc[j + 1, 1]
                recent_index = recent_index + m + 1
                break
    return df


def change_data(date0, option0, df, frequency, price_change, i, range_window):
    index1 = option0.find('-')
    code = option0[:index1] + option0[-4:]
    start_date = "2023-01-01 00:00"
    thsUrl = 'https://quantapi.51ifind.com/api/v1/high_frequency'
    thsPara = {"codes": code,
               "indicators": "open,high,low,close",
               "starttime": start_date,
               "endtime": date0,
               "functionpara": {
                   "Interval": str(frequency),
                   "Fill": "Original"
               }
               }
    thsResponse = requests.post(url=thsUrl, json=thsPara, headers=thsHeaders)
    data = json.loads(thsResponse.content)
    df1 = pd.json_normalize(data['tables'])
    try:
        df2 = df1.set_index(['thscode'])
    except Exception as e:
        print(data['errmsg'])
        print(thsPara)
        sys.exit()
    un_nested_list = []
    for col in df2.columns:
        un_nested_list.append(df2[col].apply(pd.Series).stack())
    data = pd.concat(un_nested_list, axis=1, keys=df2.columns)
    # 设置二级索引
    data = data.reset_index()
    data = data.set_index(['thscode', 'time'])
    # 格式化,行转列
    data = data.drop(columns=['level_1'])
    data = data.reset_index()
    # 设置列索引名称
    data.columns = ['thscode', 'time', 'close', 'open', 'high', 'low']
    # 去除NaN值
    data.dropna(inplace=True)
    data = data.reset_index()
    data = data.iloc[:, 1:]
    data_temp = data.iloc[-1-range_window:-1, ]
    data_temp = data_temp.reset_index(drop=True)
    # 计算上下界限
    upper = data_temp['high'].max()
    lower = data_temp['low'].min()
    # 计算期权名称的执行价，有时候是多少以下一个变化，多少以上另一个变化，可以读取进来两个，根据值来判断，还需要设置一个（没有实现）
    UpDown = upper - lower
    otm_range = int(UpDown / 4)
    price1 = round(data_temp.iloc[-1, 2] + otm_range)
    strike_price1 = int(price1 / price_change) * price_change
    while abs(strike_price1 - price1) > (price_change / 2):
        strike_price1 = strike_price1 + price_change
    K_call = int(strike_price1)
    price2 = round(data_temp.iloc[-1, 2] - otm_range)
    strike_price2 = int(price2 / price_change) * price_change
    while abs(strike_price2 - price2) > (price_change / 2):
        strike_price2 = strike_price2 + price_change
    K_put = int(strike_price2)
    df.iloc[i, 6] = K_call
    df.iloc[i, 7] = K_put
    df.iloc[i, 0] = option0[:index1] + '-C-' + str(K_call) + option0[-4:]
    df.iloc[i, 1] = option0[:index1] + '-P-' + str(K_put) + option0[-4:]
    df.iloc[i, 3] = data_temp.iloc[-1, 2]
    df.iloc[i, 4] = upper
    df.iloc[i, 5] = lower
    return df


def Data_Calculate(df, frequency, profit_stop, loss_stop, quant0, account, rate, contract_multiplier, date_frame,
                   price_change, range_window):
    print("计算数据阶段")
    df[['action', 'call_sell_price', 'call_market_price', 'average_call_change', 'put_sell_price', 'put_market_price',
        'average_put_change', 'sum_change', 'account']] = np.zeros((len(df), 9))
    df = df.sort_values(by='time', ascending=True)
    df = df.reset_index()
    df = df.iloc[:, 1:]
    # action在最后的表中是列8
    # action表示的是我每一次操作，count是临时记录用（应对到期的期权），sum_income是总收入
    # number_of_profit_exits是止盈的次数，number_of_stop_losses是止损的次数
    action = 1
    length = len(df)
    sum_income = 0
    number_of_profit_exits = 0
    number_of_stop_losses = 0
    flag = 0
    i = 0
    while i < length:
        df.iloc[i, 8] = action
        # 根据action的值来做出相应的判断，第一次进来特殊判断
        if i == 0 or df.iloc[i, 8] != df.iloc[i - 1, 8]:
            # 判断是不是期权存在问题，包括数据的读取问题，假定所有的问题都能通过更换期权合约来解决
            try:
                # 看涨保证金的价格的获取
                index1 = str(df.iloc[i, 2]).find(' ')
                str_date = str(df.iloc[i, 2])[:index1]
                deposit1 = get_the_margin_price(str_date, df.iloc[i, 0])
                # 看跌保证金的价格的获取
                deposit2 = get_the_margin_price(str_date, df.iloc[i, 1])
                # 获取看涨数据
                df = get_the_option_data(df.iloc[i, 0], df.iloc[i, 2], df.iloc[-1, 2], frequency, i, df,
                                         date_frame, 0)
                # 获取看跌数据
                df = get_the_option_data(df.iloc[i, 1], df.iloc[i, 2], df.iloc[-1, 2], frequency, i, df,
                                         date_frame, 1)
                # 形成临时价格
                if i == 0:
                    temp_price = account
                    df.iloc[0, 16] = account
                else:
                    temp_price = df.iloc[i - 1, 16]
                # 计算最多可以卖多少对
                lots = int(temp_price * rate / (deposit1 + deposit2))
            except Exception as e:
                # 更换一个新的期权合约
                df = change_data(df.iloc[i, 2], df.iloc[i, 0], df, frequency, price_change, i, range_window)
                flag = 1
                continue
            if lots == 0:
                print("现在的账户总额不足以卖出一对期权合约")
                sys.exit()
            quant = min(quant0, lots)
            # 建立临时list
            arr1 = np.zeros(quant, dtype=float)
            arr2 = np.zeros(quant, dtype=float)
            temp = quant
            df.iloc[i, 9] = df.iloc[i, 10]
            df.iloc[i, 12] = df.iloc[i, 13]
            # 对临时list进行填充
            arr1[0] = df.iloc[i, 10]
            arr2[0] = df.iloc[i, 13]
            temp = temp - 1
        elif df.iloc[i, 8] == df.iloc[i - 1, 8]:
            # 判断当前的日期在当前的期权是不是过期的
            if df.iloc[i, 10] == 0 and df.iloc[i, 13] == 0:
                flag = 0
                action = action + 1
                continue
            # 清空表
            if flag == 1:
                df.iloc[i, 3] = np.nan
            df.iloc[i, 0] = np.nan
            df.iloc[i, 1] = np.nan
            df.iloc[i, 4] = np.nan
            df.iloc[i, 5] = np.nan
            df.iloc[i, 6] = np.nan
            df.iloc[i, 7] = np.nan
            # 实现多批的功能
            if temp == 0:
                df.iloc[i, 9] = np.nan
                df.iloc[i, 12] = np.nan
            else:
                df.iloc[i, 9] = df.iloc[i, 10]
                df.iloc[i, 12] = df.iloc[i, 13]
                arr1[quant - temp] = df.iloc[i, 10]
                arr2[quant - temp] = df.iloc[i, 13]
                temp = temp - 1
        # 计算参数，填表
        sum1 = 0
        sum2 = 0
        number = 0
        for j in range(quant):
            if arr1[j] != 0:
                sum1 = sum1 + arr1[j]
                sum2 = sum2 + arr2[j]
                number = number + 1
        average1 = float(sum1) / number
        average2 = float(sum2) / number
        df.iloc[i, 11] = average1 - df.iloc[i, 10]
        df.iloc[i, 14] = average2 - df.iloc[i, 13]
        df.iloc[i, 15] = (df.iloc[i, 11] + df.iloc[i, 14]) * contract_multiplier
        # 判断条件：指的是两个期权卖出去的价差总和除以两个期权卖价总和，如果大于Profit_Taking_Standard或者小于Stop_Loss_Standard
        if df.iloc[i, 15] >= 0:
            df.iloc[i, 16] = temp_price + df.iloc[i, 15] * number
            if (df.iloc[i, 15] * number) / ((sum1 + sum2) * contract_multiplier) >= profit_stop:
                number_of_profit_exits = number_of_profit_exits + 1
                flag = 0
                action = action + 1
        else:
            df.iloc[i, 16] = temp_price + df.iloc[i, 15] * number
            if (df.iloc[i, 15] * number) / ((sum1 + sum2) * contract_multiplier) <= loss_stop:
                number_of_stop_losses = number_of_stop_losses + 1
                flag = 0
                action = action + 1
        if i == length - 1:
            sum_income = df.iloc[i, 16] - df.iloc[0, 16]
        i = i + 1
    cols_to_delete = ['average_call_change', 'average_put_change']
    df_dropped = df.drop(columns=cols_to_delete, axis=1)
    return sum_income, df_dropped, number_of_profit_exits, number_of_stop_losses


def backtest(code, start_date, end_date, local_path=None, frequency=30, profit_stop=0.1, loss_stop=-0.1, quant=1,
             account=10000, rate=0.5, save_path=None, range_window=300):
    # 读取所选期货数据
    data = Import_Futures_Data(code, start_date, end_date, frequency)
    # 读取本地的数据
    price_change, key_months, contract_multiplier = Import_Local_Data(code, local_path)
    # 通过期货选择期权
    df, date_frame = Identifying_Options(data, code, price_change=price_change, key_months=key_months,
                                         range_window=range_window)
    # 计算整个数据表，得到最终的结果
    income, result, number_of_profit_exits, number_of_stop_losses = Data_Calculate(df, frequency, profit_stop,
                                                                                   loss_stop, quant, account, rate,
                                                                                   contract_multiplier, date_frame,
                                                                                   price_change, range_window)
    # 对结果进行一个总的输出和保存
    result = result.reset_index()
    result = result.iloc[:, 1:]
    annualized_yield = income / account
    percent_string = "{:.2f}%".format(annualized_yield * 100)
    win = number_of_profit_exits / (number_of_profit_exits + number_of_stop_losses)
    strings = ["选择的期货为" + code, "开始时间为" + result.iloc[0, 2] + "  结束时间为" + result.iloc[-1, 2],
               "时间的频率为" + str(frequency) + "分钟",
               "止盈的比例为" + str(profit_stop) + "  止损的比例为" + str(loss_stop),
               "止盈的次数为" + str(number_of_profit_exits) + "次  止损的比例为" + str(number_of_stop_losses) + "次",
               "起始资金为" + str(account) + "元", "风险度为" + str(rate),
               "总收入为" + str(income) + "元", "胜率为" + str(win), "年化收益率为" + percent_string]
    df = pd.DataFrame(strings, columns=['Parameter'])
    if save_path is None:
        save_path = "./期货" + code + "回测数据结果表.xlsx"
    with pd.ExcelWriter(save_path, engine='openpyxl') as writer:
        result.to_excel(writer, sheet_name='sheet1', index=False)
        df.to_excel(writer, sheet_name='Sheet2', index=False)
    print("起始资金为：", account, "账户最后的总额为：", income + account)


def main():
    # 输入参数
    code = input("请输入期货标的（期货活跃连续合约），格式为 【期货代码】 + 【主连】 + 【交易所代码】，比如螺纹钢：RBZL.SHF ")
    frequency = int(input("请输入行情的频率, 60分钟则输入60 "))
    start_date = input("请输入起始时间，（格式YYYY-MM-DD hh:mm:ss）")
    if len(start_date) == 10:
        start_date = start_date + " 00:00:00"
    end_date = input("请输入截至时间，（格式YYYY-MM-DD hh:mm:ss）")
    if len(end_date) == 10:
        end_date = end_date + " 00:00:00"
    account = int(input("请输入起始资金（正整数）"))
    risk = float(input("请输入风险度（0 - 1之间的小数）"))
    profit_stop = float(input("请输入止盈线（0 - 1之间的正小数）"))
    loss_stop = float(input("请输入止损线（0 - 1之间的负小数）"))
    quant = int(input("请输入分批处理的批数（正整数），只做一批：1，做多批：>1 "))
    window = int(input("请输入滑动窗口的大小（大于0的正整数） "))
    local_path = input(
        "请输入读取本地文件的地址，比如 D:\大四下\实习\大地期货\\期权转换表.xlsx,如果没有则默认读取当前目录下面的文件")
    if local_path == "":
        local_path = "./期权转换表.xlsx"
    save_path = input(
        "请输入保存结果文件的地址，比如 D:\大四下\实习\大地期货\测试\回测数据结果表.xlsx,如果没有则默认保存在当前目录下")
    if save_path == "":
        save_path = None
    backtest(code, start_date, end_date,
             local_path=local_path, frequency=frequency, profit_stop=profit_stop, loss_stop=loss_stop,
             quant=quant, account=account, rate=risk, save_path=save_path, range_window=window)
    # 对于异常的数据，使用不同的窗口值来测试50为一个间隔(300，250，200，150)


if __name__ == '__main__':
    main()
